package com.gzs.learn.payment.util;


import com.gzs.learn.common.util.JsonUtil;
import com.gzs.learn.payment.inf.exception.PaymentException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
public class CommonsUtil {

    /**
     * 目标对象赋值
     *
     * @param targetObject
     * @param value
     * @param descriptor
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     */
    public static void simpleTypeAdapter(Object targetObject, String value, PropertyDescriptor descriptor)
            throws IllegalAccessException, InvocationTargetException {
        Method readeMethod = descriptor.getReadMethod();
        String retrunType = readeMethod.getReturnType().getName();
        Method writeMethod = descriptor.getWriteMethod();
        if ("java.lang.String".equals(retrunType)) {
            writeMethod.invoke(targetObject, value);
        } else if ("java.lang.Byte".equals(retrunType)) {
            writeMethod.invoke(targetObject, Byte.valueOf(value));
        } else if ("java.lang.Short".equals(retrunType)) {
            writeMethod.invoke(targetObject, Short.valueOf(value));
        } else if ("java.lang.Integer".equals(retrunType)) {
            writeMethod.invoke(targetObject, Integer.valueOf(value));
        } else if ("java.lang.Float".equals(retrunType)) {
            writeMethod.invoke(targetObject, Float.valueOf(value));
        } else if ("java.lang.Double".equals(retrunType)) {
            writeMethod.invoke(targetObject, Double.valueOf(value));
        } else if ("java.lang.Long".equals(retrunType)) {
            writeMethod.invoke(targetObject, Long.valueOf(value));
        } else {
        }
    }

    /**
     * 微信/支付宝/银联token转换
     *
     * @param origin
     * @param clazz
     * @return
     */
    public static <T> T convertToken(String origin, Class<T> clazz) {
        try {
            String json = EncryptUtil.aesDecrypt(origin);
            return JsonUtil.parseObject(json, clazz);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 对象转换为xml
     *
     * @param obj 待转换的对象
     * @param transField 需要转义的属性列表
     * @return
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     * @throws IntrospectionException
     */
    public static String objToXml(Object obj, String... transField) throws Exception {
        Class<?> clazz = obj.getClass();
        Field[] fields = clazz.getDeclaredFields();
        StringBuilder sb = new StringBuilder();
        sb.append("<xml>");
        for (Field f : fields) {
            f.setAccessible(true);
            // 静态字段忽略
            if (Modifier.isStatic(f.getModifiers())) {
                continue;
            }
            String fieldName = f.getName();

            PropertyDescriptor descriptor = new PropertyDescriptor(fieldName, clazz);
            Method method = descriptor.getReadMethod();
            Object fieldValue = method.invoke(obj);
            // 空的参数忽略
            if (null == fieldValue || StringUtils.isBlank(fieldValue.toString())) {
                continue;
            }
            sb.append("<").append(fieldName).append(">");
            if (transField != null) {
                Arrays.sort(transField);
                if (Arrays.binarySearch(transField, fieldName) >= 0) {
                    sb.append("<![CDATA[");
                    sb.append(fieldValue);
                    sb.append("]]>");
                } else {
                    sb.append(fieldValue);
                }
            } else {
                sb.append(fieldValue);
            }
            sb.append("</").append(fieldName).append(">");
            sb.append("\n");
        }
        sb.append("</xml>");
        return sb.toString();
    }

    /**
     * xml串转换为对象工具类
     *
     * @param xml
     * @param clz
     * @return
     * @throws Exception
     */
    public static <T> T xmlToObj(String xml, Class<T> clz) throws Exception {
        if (StringUtils.isBlank(xml)) {
            return null;
        }
        Document document = DocumentHelper.parseText(xml);
        Element root = document.getRootElement();
        Iterator<Element> it = root.elementIterator();
        T obj = clz.getDeclaredConstructor().newInstance();
        while (it.hasNext()) {
            Element element = it.next();
            String name = element.getName();
            String text = element.getText();
            try {
                PropertyDescriptor descriptor = new PropertyDescriptor(name, clz);
                simpleTypeAdapter(obj, text, descriptor);
            } catch (Exception e) {
                // ignore error
                // log.error("convert property[{}] with value:{} error", name, text);
            }
        }
        return obj;
    }

    public static Map<String, String> xmlToMap(String xml) throws Exception {
        if (StringUtils.isBlank(xml)) {
            return null;
        }
        Document document = DocumentHelper.parseText(xml);
        Element root = document.getRootElement();
        Iterator<Element> it = root.elementIterator();
        Map<String, String> map = new HashMap<>();
        while (it.hasNext()) {
            Element element = it.next();
            String name = element.getName();
            String text = element.getText();
            map.put(name, text);
        }
        return map;
    }

    /**
     * 对象属性的读方法
     *
     * @param filedName
     * @param cls
     * @return
     * @throws IntrospectionException
     */
    public static Method getGetMethod(String filedName, Class<?> cls) throws IntrospectionException {
        PropertyDescriptor descriptor = new PropertyDescriptor(filedName, cls);
        return descriptor.getReadMethod();
    }

    /**
     * 对象属性的写方法
     *
     * @param filedName
     * @param cls
     * @return
     * @throws IntrospectionException
     */
    public static Method getSetMethod(String filedName, Class<?> cls) throws IntrospectionException {
        PropertyDescriptor descriptor = new PropertyDescriptor(filedName, cls);
        return descriptor.getWriteMethod();
    }

    /**
     * controller 结果转换为对象
     *
     * @param httpResult
     * @param cls
     * @return
     */
    public static <T> T httpResultToObj(String httpResult, Class<T> cls) {
        if (StringUtils.isBlank(httpResult))
            return null;
        String[] arrData = httpResult.split("&");
        if (null == arrData || arrData.length <= 0) {
            return null;
        }
        try {
            T result = cls.getDeclaredConstructor().newInstance();
            for (String pair : arrData) {
                if (StringUtils.isBlank(pair)) {
                    continue;
                }
                if (!pair.contains("=")) {
                    continue;
                }
                int pos = pair.indexOf("=");
                String name = pair.substring(0, pos);
                String text = pair.substring(pos + 1);
                PropertyDescriptor descriptor = new PropertyDescriptor(name, cls);
                simpleTypeAdapter(result, text, descriptor);
            }
            return result;
        } catch (Exception e) {
            throw new PaymentException(e);
        }
    }

    /**
     * map类型转换为对象
     *
     * @param params
     * @param cls
     * @return
     */
    public static <T> T map2Obj(Map<String, String> params, Class<T> cls) {
        if (params == null || params.isEmpty()) {
            return null;
        }
        try {
            T result = cls.getDeclaredConstructor().newInstance();
            for (Entry<String, String> entry : params.entrySet()) {
                String propertyName = entry.getKey();
                String propertyValue = entry.getValue();
                if (StringUtils.isBlank(propertyName) || StringUtils.isBlank(propertyValue)) {
                    continue;
                }
                PropertyDescriptor descriptor = new PropertyDescriptor(propertyName, cls);
                simpleTypeAdapter(result, propertyValue, descriptor);
            }
            return result;
        } catch (Exception e) {
            throw new PaymentException(e);
        }
    }

    /**
     * 对象转换为http请求参数
     *
     * @param o
     * @param ignoreProperty
     * @return
     */
    public static String obj2UrlParam(Object o, String... ignoreProperty) {
        if (null == o) {
            return null;
        }
        Class<?> clazz = o.getClass();
        Field[] fields = clazz.getDeclaredFields();
        String result = Stream.of(fields).filter(f -> {
            return filterProperty(o, f, ignoreProperty);
        }).map(f -> {
            return linkFieldWithUrlEncode(o, f, "=");
        }).sorted().collect(Collectors.joining("&"));
        return result;
    }

    /**
     * 过滤对象中的属性
     *
     * @param obj
     * @param field
     * @param ignoreProperty
     * @return
     */
    public static boolean filterProperty(Object obj, Field field, String... ignoreProperty) {
        try {
            field.setAccessible(true);
            if (Modifier.isStatic(field.getModifiers())) {
                return false;
            }
            if (ignoreProperty != null && ignoreProperty.length > 0) {
                Arrays.sort(ignoreProperty, String.CASE_INSENSITIVE_ORDER);
                if (Arrays.binarySearch(ignoreProperty, field.getName()) >= 0) {
                    return false;
                }
            }
            Method getMethod = getGetMethod(field.getName(), obj.getClass());
            Object fieldValue = getMethod.invoke(obj);
            return fieldValue != null && !fieldValue.equals("");
        } catch (Exception e) {
            log.error("refelct error", e);
            return false;
        }
    }

    private static String linkFieldWithUrlEncode(Object o, Field f, String linker) {
        Object fieldValue;
        try {
            Method getMethod = getGetMethod(f.getName(), o.getClass());
            fieldValue = getMethod.invoke(o);
            String val = URLEncoder.encode(fieldValue.toString(), StandardCharsets.UTF_8).replaceAll("\\+", "%20");
            return f.getName() + linker + val;
        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | IntrospectionException e) {
            log.error("link field error", e);
            return "";
        }
    }

    /**
     * 生成指定位数的随机数
     *
     * @param randomLen
     * @return
     * @see org.apache.commons.lang3.RandomStringUtils
     */
    public static String generateRandom(int randomLen) {
        return RandomStringUtils.randomNumeric(randomLen);
    }
}
